package com.rc.saas.tenant.shiro;

import com.rc.saas.tenant.common.constant.ShiroConstants;
import com.rc.saas.tenant.common.constant.WebConstant;
import com.rc.saas.tenant.model.tenant.TenantMenu;
import com.rc.saas.tenant.model.tenant.TenantMenuExample;
import com.rc.saas.tenant.service.tenant.TenantMenuService;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.shiro.config.Ini;
import org.apache.shiro.config.Ini.Section;
import org.apache.shiro.web.config.IniFilterChainResolverFactory;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Autowired;

import java.text.MessageFormat;
import java.util.List;

/**
 * 将数据库中的访问控制数据追加到xml配置文件的后面
 * 当项目启动时，你会发现在容器加载spring内容时，登录前就会进入到ChainDefinitionSectionMetaSource
 * Created by sven on 2017/2/19.
 */
public class CustomFilterChainDefinitions implements FactoryBean<Section> {
    private static Logger logger = LogManager.getLogger(CustomFilterChainDefinitions.class);

    //roles[test,guest]
    private static final String ROLE_STRING = "roles[{0}]";

    @Autowired
    private TenantMenuService tenantMenuService;

    //shiro默认的静态定义(写在xml上的)
    private String filterChainDefinitions;

    /**
     * 通过filterChainDefinitions对默认的链接过滤定义
     * @param filterChainDefinitions 默认的接过滤定义
     */
    public void setFilterChainDefinitions(String filterChainDefinitions) {
        this.filterChainDefinitions = filterChainDefinitions;
    }

    /*1加载类似以下的信息
            /login.do = authc
	        /favicon.ico = anon
	        /logout.do = logout
	        /selectOption.do = roles[index]
	        /index.jsp = perms[index:index]
			/testDwr.jsp = perms[index:testdwr]

	         2循环数据库资源的url
	        for (Resource resource : resourceDao.getAll()) {
	if(StringUtils.isNotEmpty(resource.getValue()) && StringUtils.isNotEmpty(resource.getPermission())) {
	        		section.put(resource.getValue(), resource.getPermission());
	        	}
	        }

			加载数据库t_permission 的 value 和 permission组成类似1的格式 ，
			若要这样使用， permission 需要--->  perms[permission]
	*/

    @Override
    public Section getObject() throws BeansException {
        // 加载资源文件节点串定义的初始化权限信息
        //Ini ini = new Ini();
        //ini.load(filterChainDefinitions);
        //logger.info("加载静态filterChainDefinitions:{}", filterChainDefinitions);

        //将静态配置的资源与要添加的动态资源整合在一起，生成shiro使用的权限map
        //Section section = ini.getSection(Ini.DEFAULT_SECTION_NAME);
        Section section = loadSection();

        //直接使用shiro的登出
        //section.put("/logout", "logout");

        //查询数据库中URL对应需要的角色权限
        //List<TenantRole> list = tenantRoleService.selectRoleMenu();
        List<TenantMenu> list = tenantMenuService.selectByExample(new TenantMenuExample());

        logger.info("加载动态filterChainDefinitions:{}", list.size());

        //authc:所有url都必须认证通过才可以访问
        //格式:/security/**=authc

        //注意：要拦截的链接url,如果url没有授权给任何role,那么这个url相当任何人都能访问
        //在构建头部导航菜单时（只显示有授权的菜单），所以在这里只作授权，不作显示与否

        //roles：例子/admins/user/**=roles[admin],参数可以写多个，多个时必须加上引号，并且参数之间用逗号分割，
        // 当有多个参数时，例如admins/user/**=roles["admin,guest"],每个参数通过才算通过，相当于hasAllRoles()方法

        for (TenantMenu menu : list) {
            //roles[test]

            //适合用户类型 0 运营商 1 分销商 2企业帐户  默认0
            String authType;
            if (menu.getUserType().equals(WebConstant.STATUS_ZERO)) {
                authType = MessageFormat.format(ROLE_STRING, ShiroConstants.TENANT_ROLE_NAME);
            } else if (menu.getUserType().equals(WebConstant.STATUS_ONE)) {
                authType = MessageFormat.format(ROLE_STRING, ShiroConstants.OPERATOR_ROLE_NAME);
            } else if (menu.getUserType().equals(WebConstant.STATUS_TWO)) {
                authType = MessageFormat.format(ROLE_STRING, ShiroConstants.ENTERPRISE_ROLE_NAME);
            } else {
                authType = "";
            }

            //绑定URL=角色对照关系
            if (menu != null && StringUtils.isNotBlank(menu.getHref())) {
                section.put(menu.getHref(), authType);
            }
        }

        /*因为顺序原因, 把/**放到最后
         *   [上面的覆盖下面的]
         *  把("/**", "authc") 放在  ("/testDwr.jsp", "perms[index:testdwr]")  上面,
         *  /testDwr.jsp 就只需要登录, 不需要perms[index:testdwr]权限了
         */

        //过滤链定义，从上向下顺序执行，一般将 /**放在最为下边
        //section.put("/**", "anon");
        section.put("/**", "authc");

        for (String s : section.keySet()) {
            logger.info(":{} = :{}", s, section.get(s));
        }

        return section;
    }

    /**
     * 加载静态过虑器链（上面的覆盖下面的）定义
     * @return
     */
    private Section loadSection() {
        Ini ini = Ini.fromResourcePath("classpath:config/shiro.ini");
        Section section = ini.getSection(IniFilterChainResolverFactory.URLS);
        return section;
    }

    @Override
    public Class<?> getObjectType() {
        return Section.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

}

